home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The PC-SIG Library 10
/
The PC-Sig Library - Shareware for the IBM PC and Compatibles (PC-SIG)(Tenth Edition Disks 1-2804)(1991).iso
/
PC_SIGCD
/
05
/
6
/
DISK0564.ZIP
/
SOURCE.ARC
/
MV.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-04-02
|
42KB
|
1,028 lines
TITLE MV - MSDOS FILE MOVE UTILITY
PAGE 55,132
;***************************************************************************
; THIS PROGRAM RENAMES A FILE OR DIRECTORY, OR MOVES ONE OR MORE FILES
; OR DIRECTORIES BETWEEN DISKS OR DIRECTORIES.
;
; USAGE (afn = ambiguous file name, ufn = unambiguous file name,
; dir = directory):
;
; MV ufn1 ufn2 - renames or moves ufn1 to ufn2.
;
; MV afn1 afn2 ... dir - moves one or more files into dir.
;
; MV -R dir1 dir2 if dir2 doesn't exist, renames or moves
; dir1 to dir2.
; (note: DOS 2 requires that renaming a direc-
; tory be done by creating dir2, moving all
; files from dir1 into it, then deleting dir1).
;
; - if dir2 exists, makes dir1 a subdirectory
; of it.
;
; SWITCHES (OPTIONAL):
;
; I prompts before overwriting files
;
; R allows recursively moving directories.
;
; V turns on "verbose" mode (shows name of each file as it
; is moved).
;***************************************************************************
; BY: JON DART
; 3012 HAWTHORN ST.,
; SAN DIEGO, CA 92104
;***************************************************************************
; TO BUILD MV.EXE:
; MASM MV,MV,NUL,NUL
; LINK MV,MV,NUL,ASM
; EXEPACK MV.EXE MV.NEW
; DEL MV.EXE
; REN MV.NEW=MV.EXE
;***************************************************************************
; Version 3.3, 30-Mar-89 moves with root directory as destination
; now handled correctly. Also a bug fix in
; procedure SETUP_SRC_DIR.
; Version 3.2, 19-Jan-89 fix to FIXPATH; relinked
; Version 3.1, 03-Nov-88 disallows MV afn foo if foo doesn't exist.
; Version 3.0, 24-Sep-88 bug fix in FIXPATH; relinked
; Version 2.5, 09-Feb-88 assembles under MASM 5.0
; Version 2.4, 30-May-87 fixes a bug causing file deletion when source
; is in current directory and dest is omitted.
; Version 2.3, 28-Nov-86 fixes bug involving relative directory moves
; (e.g. mv -r ..\foo .)
; Version 2.2, 24-Oct-86 switch parser allows -rvi as well as -r -v -i
; Version 2.1, 06-Jul-86 changes to memory alloc, allows hyphen for
; switches, now assembles under masm 4.0
; Version 2.0, 30-Jun-86 minor cleaning up, r and i switches added, c
; switch removed
; Version 1.9, 26-Apr-86 doesn't exit after user responds n to
; overwrite msg.
; Version 1.8, 31-Mar-86 more bug fixes (*sigh*)
; Version 1.7, 29-Mar-86 bug fixes
; Version 1.6, 24-Mar-86 allows multiple source files
; Version 1.5, 25-Jan-86 adds check for identical source & dest
; Version 1.4, 14-Jan-86 adds switches
; Version 1.3, 12-Jan-86 adds recursion
; Version 1.2, 01-Jan-86
; Version 1.1, 01-Nov-85
; Version 1.0, 29-Sep-85
;***************************************************************************
DOSSEG
.MODEL SMALL
PATHSIZE EQU 65 ;MAX. SIZE OF DOS PATHNAME, +1
BUFSIZE EQU 32*1024 ;SIZE OF COPY BUFFER
MAXLEVEL EQU 12 ;MAX NESTING LEVEL FOR DIRECTORIES
MAXARGS EQU 40 ;MAXIMUM # OF COMMAND LINE ARGUMENTS
TRUE EQU 0FFH
FALSE EQU 0
CONFIRM EQU FALSE ;"CONFIRM BEFORE OVERWRITE" OPTION (DEFAULT VALUE)
RECURSE EQU FALSE ;"RECURSE" OPTION (DEFAULT VALUE)
VERBOSE EQU FALSE ;"VERBOSE" OPTION (DEFAULT VALUE)
M EQU BYTE PTR 0[BX]
BIT$DIR EQU 00010000B ;BIT IN ATTRIBUTE FOR DIRECTORY
BIT$RO EQU 00000001B ;BIT IN ATTRIBUTE FOR WRITE-PROTECT
.XLIST
INCLUDE ASCII.DEF
INCLUDE MSDOS2.DEF
INCLUDE MACROS.DEF
.LIST
MVINFO STRUC
NEWDIR DW 1 DUP (?) ;FLAG, =1 IF NEW DIRECTORY CREATED
SRCTYPE DB 2 DUP (?) ;TYPE OF SOURCE (0 IF UFN)
DESTTYPE DB 2 DUP (?) ;TYPE OF DEST (0 IF UFN)
ARG1 DB PATHSIZE DUP (?) ;FIRST ARGUMENT (SOURCE)
ARG2 DB PATHSIZE DUP (?) ;SECOND ARGUMENT (DEST)
ARG2A DB PATHSIZE DUP (?) ;SOURCE PREFIX + DEST
SSPATH DB PATHSIZE DUP (?) ;SOURCE SEARCH PATH
SPREFIX DB PATHSIZE DUP (?) ;SOURCE LEAD-IN PATH
DSPATH DB PATHSIZE DUP (?) ;DEST SEARCH PATH
;(USED ONLY AS SCRATCH AREA)
DPREFIX DB PATHSIZE DUP (?) ;DEST LEAD-IN PATH
SRCNAME DB PATHSIZE DUP (?) ;UNAMBIGUOUS SOURCE NAME
DESTNAME DB PATHSIZE DUP (?) ;UNAMBIG. DEST NAME
DTA DB 128 DUP (?) ;DTA FOR SEARCH FN.
MVINFO ENDS
MVSTRUCSIZE EQU 9*PATHSIZE+135 ;SIZE OF STRUCTURE
.DATA
; INITIALIZED DATA STORAGE:
;
MSG1 DB 'MV Version 3.3 by Jon Dart (30-Mar-89)',CR,LF,CR,LF
DB 'USAGE (afn = ambiguous file name, ufn = unambiguous file name,'
DB CR,LF
DB ' dir = directory):',CR,LF,CR,LF
DB 'MV ufn1 ufn2 move or rename ufn1 to ufn2.',CR,LF,CR,LF
DB 'MV afn1 afn2 ... dir move one or more files into dir.',CR,LF,CR,LF
DB "MV -R dir1 dir2 if dir2 doesn't exist, move or rename"
DB CR,LF
DB ' dir1 to dir2.',CR,LF,CR,LF
DB ' if dir2 exists, make dir1 a subdirectory'
DB CR,LF
DB ' of it.',CR,LF,CR,LF
DB 'SWITCHES (OPTIONAL):',CR,LF,CR,LF
DB '-I prompt before overwriting files.',CR,LF,CR,LF
DB '-R allow recursive moving of directories.',CR,LF,CR,LF
DB '-V echo file names as they are moved.',CR,LF
DB 0
MSG2 DB ': file not found.',CR,LF,0
MSG3 DB 'Source and dest are incompatible.',CR,LF,0
MSG4 DB ' exists. Overwrite [Y or N]? ',0
MSG5 DB ': open failed.',CR,LF,0
MSG6 DB ': file is R/O. Are you SURE [Y or N]? ',0
MSG7 DB ': directory.',CR,LF,0
MSG8 DB ': write error.',CR,LF,0
MSG9 DB '; read error.',CR,LF,0
MSG10 DB ': write error (probably disk full).',CR,LF,0
MSG11 DB ': rename failed.',CR,LF,0
MSG12 DB ": can't create.",CR,LF,0
MSG13 DB ": can't remove.",CR,LF,0
MSG14 DB "Directories nested too deep.",CR,LF,0
; MSG15 DB "Insufficient memory for buffers.",CR,LF,0
MSG16 DB ": Illegal switch.",CR,LF,0
MSG17 DB "Error: source and dest are identical.",CR,LF,0
MSG18 DB "Too many arguments.",CR,LF,0
SWLIST DB 'IRV',0 ;LIST OF LEGAL SWITCHES
.STACK
DB 512 DUP (?)
.DATA?
; UNINITIALIZED DATA AND BUFFER AREAS
RBASE DW 1 DUP (?) ;POINTER TO MVSTRUC IN RSTACK
LEVEL DW 1 DUP (?) ;RECURSION LEVEL
SRCDRIVE DW 1 DUP (?) ;SOURCE DRIVE (0 = DEFAULT)
DESTDRIVE DW 1 DUP (?) ;DEST DRIVE (0 = DEFAULT)
INHANDLE DW 1 DUP (?) ;INPUT FILE HANDLE
OUTHANDLE DW 1 DUP (?) ;OUTPUT FILE HANDLE
IFLAG DW 1 DUP (?) ;CONFIRM FLAG
RFLAG DW 1 DUP (?) ;RECURSIVE FLAG
VFLAG DW 1 DUP (?) ;VERBOSE FLAG
CMDTAIL DB 128 DUP (?) ;STORAGE FOR COMMAND TAIL
DEST_DTA DB 128 DUP (?) ;HOLDS INFO ON DEST, RETURNED FROM DOS
;FN 4E.
NUMARGS DW 1 DUP (?) ;NUMBER OF ARGUMENTS ON COMMAND LINE
ARGPTRS DW MAXARGS DUP (?) ;POINTERS TO START OF ARGUMENTS
BUFFER DB BUFSIZE DUP (?) ;FILE XFER BUFFER
RSTACK MVINFO MAXLEVEL DUP (<>) ;RECURSION STACK
PAGE +
.CODE
EXTRN CPYCNT:NEAR,UC:NEAR,UCSTR:NEAR,CRLF:NEAR,ERRORMSG:NEAR,COUT:NEAR
EXTRN CIN:NEAR,CLRCO:NEAR,SKIPSP:NEAR,TYPTX:NEAR,ERRORMSG:NEAR,CLRCO:NEAR
EXTRN CMDSRC:NEAR,FIXPATH:NEAR,GETYORN:NEAR,UNLINK2:NEAR
EXTRN TYPE_DIR:ABS,TYPE_AFN:ABS,TYPE_UFN:ABS,TYPE_DSP:ABS,TYPE_DRV:ABS
EXTRN TYPE_UNK:ABS
;*********************
;* MACRO DEFINITIONS *
;*********************
ADDR MACRO REG,OFFST ;COMPUTE ADDRESS OFFSET FROM BASE REGISTER (BX)
MOV REG,BX
ADD REG,OFFST
ENDM
ERROR MACRO ERRNUM ;SHOW ERROR MESSAGE
MOV DX,OFFSET DGROUP:MSG&ERRNUM
CALL PRTERR
ENDM
COPYPATH MACRO SOURCE,DEST ;COPY PATHNAME FROM SOURCE TO DEST
ADDR SI,SOURCE
ADDR DI,DEST
MOV CX,PATHSIZE
CALL CPYCNT
ENDM
;**********************
; PROGRAM ENTRY POINT *
;**********************
ENTRY:
TEST_DOS2 ;TEST FOR DOS 2.0, EXIT IF DOS 1
MOV AX,DGROUP
MOV ES,AX
MEMOK:
MOV BX,80H ;POINT TO BYTE COUNT FOR COMMAND LINE
CMP [BX],BYTE PTR 0
JE USEMSG ;IF NO COMMAND TAIL
PUSH BX
MOV DL,[BX]
MOV DH,0
ADD BX,DX
INC BX
MOV [BX],BYTE PTR 0 ;PUT 0 BYTE AT END OF COMMAND LINE
POP BX
INC BX
CALL UCSTR ;CONVERT COMMAND LINE TO UPPER CASE
CALL SKIPSP ;SKIP LEADING SPACES
JNC L_2 ;IF SOMETHING ON LINE
USEMSG: ERROR 1
MOV AL,1 ;SET ERROR CODE
JMP EXIT2
L_2:
MOV SI,BX
MOV DI,OFFSET ES:CMDTAIL
MOV CX,128
CALL CPYCNT ;SAVE COMMAND TAIL
MOV AX,ES
MOV DS,AX ;SET DATA SEG TO VARIABLE AREA
MOV BYTE PTR DS:IFLAG,CONFIRM ;SET CONFIRM FLAG DEFAULT
MOV BYTE PTR DS:VFLAG,VERBOSE ;SET VERBOSE FLAG DEFAULT
MOV BYTE PTR DS:RFLAG,FALSE ;SET RECURSIVE FLAG DEFAULT
MOV WORD PTR DS:LEVEL,0 ;SET RECURSION LEVEL = 0
MOV BYTE PTR DS:SRCDRIVE,0 ;SET SOURCE DRIVE DEFAULT
MOV BYTE PTR DS:DESTDRIVE,0 ;SET DEST DRIVE DEFAULT
;***************************
; COLLECT SWITCHES, IF ANY *
;***************************
MOV BX,OFFSET CMDTAIL ;POINT TO COMMAND TAIL
NEXTSW:
CALL SKIPSP
JB USEMSG ;IF NOTHING ON LINE BESIDES SWITCHES
CMP BYTE PTR [BX],'/' ;SWITCH SPECIFIED?
JE GOTSW ;YES
CMP BYTE PTR [BX],'-' ;CHECK FOR HYPHEN, TOO
JNE NOSWITCH
GOTSW:
INC BX ;SKIP OVER SWITCH CHARACTER
CALL SKIPSP ;SKIP LEADING BLANKS
JB USEMSG ;IF NOTHING
SWLOOP:
CALL UC ;MAKE SWITCH UPPER CASE
PUSH DS
PUSH BX
PUSH AX
MOV AX,DGROUP
MOV DS,AX
POP AX
MOV BX,OFFSET SWLIST
CALL CMDSRC ;SEARCH LIST OF VALID SWITCHES
POP BX
POP DS
MOV AH,0
ADD AX,AX ;NOT FOUND IN LIST?
JZ BADSWITCH ;NO.
MOV SI,OFFSET IFLAG - 2
ADD SI,AX ;POINT TO FLAG
NOT WORD PTR [SI] ;1'S COMPLEMENT
INC BX
MOV AL,BYTE PTR [BX] ;GET NEXT CHAR. FROM LINE
CMP AL,SPACE
JE NEXTSW ;IF SPACE, LOOK FOR - OR FILENAME
CMP AL,TAB
JE NEXTSW ;IF TAB, LOOK FOR - OR FILENAME
JMP SWLOOP ;ELSE ASSUME THIS IS A SWITCH CHAR.
BADSWITCH:
MOV AL,BYTE PTR [BX]
CALL COUT ;DISPLAY BAD CHAR.
ERROR 16 ;ILLEGAL SWITCH
MOV AL,16 ;SET ERROR CODE
JMP EXIT2
;************************************************************
; SCAN THE COMMAND LINE AND STORE POINTERS TO ALL ARGUMENTS *
;************************************************************
NOSWITCH:
MOV CX,0 ;CX COUNTS # OF ARGUMENTS
MOV SI,OFFSET DS:ARGPTRS ;POINT TO ARG POINTER TABLE
SAVEARG:
INC CX ;BUMP ARGUMENT COUNT
CMP CX,MAXARGS
JG TOOMANY ;IF TOO MANY
MOV DS:[SI],BX ;SAVE POINTER TO ARG
ADD SI,2 ;ADVANCE TO NEXT TABLE ENTRY
CNTARGS:
MOV AL,DS:[BX] ;GET CHAR. FROM COMMAND LINE
CMP AL,0
JE ENDLINE ;IF END OF LINE
COMPLIST <SPACE,TAB>,NEXTARG ;SEE IF SPACE OR TAB
INC BX
JMP CNTARGS ;LOOP TILL DELIMITER FOUND
NEXTARG:
CALL SKIPSP ;SKIP SPACES AND TABS
JNB SAVEARG ;IF NOT EOL, BACK TO TOP OF LOOP
ENDLINE:
CMP CX,0 ;CHECK ARGUMENT COUNT
JE NOARGS ;IF NO ARGS
CMP CX,1
JG ARGSOK ;IF >=2 ARGS
MOV DS:[SI],BX ;EXACTLY ONE ARGUMENT, SO
INC CX ;MAKE A DUMMY (NULL) 2ND ARGUMENT
JMP SHORT ARGSOK
NOARGS:
JMP USEMSG ;ELSE USE MESSAGE
TOOMANY:
ERROR 18 ;TOO MANY ARGUMENTS (PRETTY UNLIKELY)
MOV AL,18 ;SET ERROR CODE
JMP EXIT2
;************************************
;* COLLECT THE DESTINATION PATHNAME *
;************************************
ARGSOK: MOV WORD PTR DS:NUMARGS,CX ;SAVE NUMBER OF ARGUMENTS
DEC CX ;NUMBER OF ARGS - 1
ADD CX,CX ;TIMES TWO
MOV SI,OFFSET DS:ARGPTRS
ADD SI,CX ;POINT TO ADDR. OF LAST ARGUMENT (DEST)
MOV BX,DS:[SI] ;PUT ADDR. INTO BX
MOV DI,OFFSET RSTACK.ARG2 ;POINT TO STORAGE FOR DEST
MOV AL,[BX+1]
CMP AL,':' ;CHECK FOR DEST DRIVE SPEC
JNE GETLAST ;IF NONE
MOV AL,[BX] ;DRIVE SPECIFIED, GET DRIVE LETTER
SUB AL,'A'-1 ;MAKE BINARY
MOV BYTE PTR DS:DESTDRIVE,AL ;SAVE DRIVE
GETLAST: MOV AL,[BX] ;GET A CHAR. FROM CMD LINE
COMPLIST <0,SPACE,TAB>,ENDARG2 ;IF DELIMITER
STOSB ;NOT DELIMITER, STORE IT
INC BX ;BUMP POINTER
JMP GETLAST ;LOOP
ENDARG2:
MOV AL,0
STOSB ;END DEST W. 0
;***********************************************************
;* LOOP, TAKING ONE SOURCE AT A TIME AND MOVING IT TO DEST *
;***********************************************************
MOV CX,WORD PTR DS:NUMARGS ;GET NUMBER OF ARGS AGAIN
DEC CX ;-1
MOV SI,OFFSET DS:ARGPTRS ;POINT TO POINTER TABLE
MOVE1ARG:
MOV BX,DS:[SI] ;GET POINTER TO ARG
PUSH SI
PUSH CX
CALL MOVEARG ;MOVE IT
POP CX
POP SI
ADD SI,2 ;ADVANCE TO NEXT ARG POINTER
LOOP MOVE1ARG ;LOOP TILL NO MORE ARGS TO MOVE
MOV AL,0 ;ERROR CODE 0 MEANS OK
JMP EXIT2
;**********************************
;* MOVE 1 SOURCE ARGUMENT TO DEST *
;**********************************
MOVEARG PROC NEAR
MOV BYTE PTR DS:SRCDRIVE,0 ;SET SOURCE DRIVE DEFAULT
MOV DI,OFFSET RSTACK.ARG1 ;COLLECT SOURCE
MOV AL,[BX+1]
CMP AL,':' ;CHECK FOR SOURCE DRIVE SPEC
JNE GETARG
MOV AL,[BX] ;GET DRIVE LETTER
SUB AL,'A'-1 ;MAKE BINARY
MOV BYTE PTR DS:SRCDRIVE,AL ;SAVE SOURCE DRIVE
GETARG: MOV AL,[BX] ;COPY FROM [BX] TO [DI] UNTIL DELIMITER
COMPLIST <0,SPACE,TAB>,ENDARG
STOSB
INC BX
JMP GETARG
ENDARG: MOV AL,0
STOSB ;END ARG1 W. ZERO
MOV WORD PTR DS:RBASE,OFFSET RSTACK ;SAVE OFFSET TO RSTACK
CALL MOVEIT ;DO THE STUFF
RET
MOVEARG ENDP
PAGE +
;********************************************************************
; THIS IS THE MOVE SUBROUTINE. IT ASSUMES THAT ARG1 AND ARG2 ARE
; SET UP PROPERLY IN THE RECURSION STACK AREA, AND THAT "RBASE" POINTS
; TO THE START OF THE DATA STRUCTURE FOR THIS RECURSION LEVEL.
;
MOVEIT PROC NEAR
MOV BX,WORD PTR DS:RBASE ;GET BASE ADDR. (POINTER TO MVINFO)
CALL SETUP ;PARSE ARGUMENTS, CHECK FOR ERRORS
JNC NOARGERR ;IF OK
JMP EXIT2 ;QUIT IF ERROR
;******************************************
; SEE IF ANYTHING MATCHES THE SOURCE SPEC *
;******************************************
NOARGERR:
ADDR DX,SSPATH ;POINT TO SEARCH PATH
MOV CX,31H ;SET SEARCH ATTRIBUTES
MOV AH,FIND_FIRST
INT DOS ;SEARCH FOR 1ST MATCH
JNC SRCOK ;OK IF SOMETHING FOUND
ADDR DX,ARG1
CALL ERRORMSG ;ELSE SHOW ARGUMENT
ERROR 2 ;SAY IT DOESN'T EXIST
MOV AL,2 ;SET ERROR CODE
JMP EXIT2
;**************************************
; CREATE DEST DIRECTORY, IF NECESSARY *
;**************************************
SRCOK: CMP WORD PTR [BX].NEWDIR,1 ;CHECK NEW DIRECTORY FLAG
JNE GOTONE ;IF NOT SET
ADDR DX,ARG2A ;POINT TO DEST NAME
MOV AH,MKDIR
INT DOS ;CREATE NEW DIRECTORY
JNB GOTONE ;IF OK
ADDR DX,ARG2A ;IF CAN'T CREATE,
CALL ERRORMSG ;SHOW DIRECTORY NAME
ERROR 12 ;AND ERROR MSG.
MOV AL,12 ;SET ERROR CODE
JMP EXIT2 ;EXIT 2 DOS
;**************
; TOP OF LOOP *
;**************
GOTONE:
CALL BUILD_NAMES ;MAKE UNAMBIG. SOURCE AND DEST NAMES
JNC NOSKIP
JMP NEXTFILE ;IF SOURCE IS "." OR ".." OR = DEST
NOSKIP:
CMP BYTE PTR DS:VFLAG,TRUE ;CHECK VERBOSE FLAG
JNE QUIET ;IF QUIET MODE
ADDR DX,SRCNAME ;NOT QUIET, SHOW VERBIAGE
CALL ERRORMSG
CALL TYPTX
DB ' -->',SPACE+200Q
ADDR DX,DESTNAME
CALL ERRORMSG
CALL CRLF
QUIET:
TEST [BX].DTA+21,BIT$DIR ;IS SOURCE A DIRECTORY?
JZ CHKDRIVES ;NO
;***********************************************
; IF SOURCE IS A DIRECTORY, RECURSIVELY MOVE IT*
;***********************************************
CMP BYTE PTR DS:RFLAG,TRUE ;R FLAG SET?
JNE NEXTFILE ;NO, SKIP OVER DIRECTORY
CMP WORD PTR DS:LEVEL,MAXLEVEL-1 ;ARE WE AT MAX LEVEL?
JL NOTMAX ;NO
ERROR 14 ;YES, TOO DEEP
MOV AL,14 ;SET ERROR CODE
JMP EXIT2
NOTMAX:
COPYPATH SRCNAME,(ARG1+MVSTRUCSIZE) ;COPY SOURCE DIRECTORY TO ARG1 AT NEXT LEVEL
COPYPATH DESTNAME,(ARG2+MVSTRUCSIZE) ;COPY DEST NAME TO ARG2 AT NEXT LEVEL
PUSH BX ;SAVE POINTER TO BASE
INC WORD PTR DS:LEVEL ;INCREMENT RECURSION LEVEL
ADD WORD PTR DS:RBASE,MVSTRUCSIZE ;ADVANCE BASE POINTER
CALL MOVEIT ;MOVE THE DIRECTORY
POP BX ;RESTORE BASE POINTER
DEC WORD PTR DS:LEVEL ;DECREMENT RECURSION LEVEL
SUB WORD PTR DS:RBASE,MVSTRUCSIZE ;RESTORE BASE TO PREVIOUS LEVEL
ADDR DX,DTA
MOV AH,SET_DTA
INT DOS ;RESET DTA
JMP SHORT NEXTFILE ;DO NEXT FILE
;*****************************************
; IF SOURCE IS A FILE, COPY OR RENAME IT *
;*****************************************
CHKDRIVES:
MOV AL,BYTE PTR DS:SRCDRIVE ;GET SOURCE DRIVE
CMP AL,BYTE PTR DS:DESTDRIVE ;COMPARE W. DEST..
JNE MOVEM
CALL RENAME_FILE ;SAME DRIVE, JUST RENAME
JNB NEXTFILE
JMP EXIT2 ;ERROR, EXIT TO DOS
MOVEM: CALL COPY_FILE ;DIFFERENT DRIVE, COPY SOURCE TO DEST
JNB NEXTFILE
JMP EXIT2 ;ERROR, EXIT TO DOS
;*****************
; BOTTOM OF LOOP *
;*****************
NEXTFILE:
MOV AH,FIND_NEXT
INT DOS ;FIND NEXT MATCH, IF ANY
JC NOMORE ;IF NONE
JMP GOTONE ;GOT ONE, BACK TO TOP OF LOOP
;**********************
; NO MORE FILES, DONE *
; *********************
NOMORE: MOV AX,WORD PTR [BX].SRCTYPE ;GET TYPE OF SOURCE
CMP AX,TYPE_DIR ;DIRECTORY?
JNE JUSTCLOSE ;NO
ADDR DX,ARG1 ;YES, POINT TO NAME
MOV AH,RMDIR
INT DOS ;REMOVE IT
JNB JUSTCLOSE
ADDR DX,ARG1
CALL ERRORMSG
ERROR 13 ;COMPLAIN IF ERROR
CALL CLOSE2 ;CLOSE OUTPUT FILE
MOV AL,13 ;SET ERROR CODE
JMP EXIT2 ;EXIT TO DOS
;**************
; NORMAL EXIT *
;**************
JUSTCLOSE:
CALL CLOSE2 ;CLOSE OUTPUT FILE
CALL CLOSE1 ;CLOSE INPUT FILE
RET ;ALL DONE
MOVEIT ENDP
PAGE +
;*************************************************************************
; THIS PROCEDURE PARSES ARG1 AND ARG2 AND CHECKS FOR INCOMPATIBLE TYPES
; (E.G. "MV FOOBAR *.*"). IF SUCCESSFUL, IT SETS SSPATH = SOURCE SEARCH
; PATH, SPREFIX = SOURCE PREFIX, DSPATH = DEST SEARCH PATH (NOT USED),
; DPREFIX = DEST PREFIX, SRCTYPE = SOURCE TYPE DESTTYPE = DESTINATION TYPE.
; IT ALSO SETS "NEWDIR"=1 IF THE DESTINATION DIRECTORY MUST BE CREATED.
; ALL OF THESE VARIABLES ARE IN A STRUCTURE OF TYPE 'MVINFO' IN THE RECURSION
; STACK AREA (STARTING AT THE ADDRESS IN 'RBASE').
;
; ON RETURN, THE CARRY FLAG IS SET IF AN ERROR OCCURRED, AND AL HOLDS AN
; ERROR CODE KEYED TO THE ERROR MESSAGE NUMBER.
;
SETUP PROC NEAR
MOV BX,WORD PTR DS:RBASE ;GET ADDR. OF DATA STRUCTURE
MOV WORD PTR [BX].NEWDIR,0 ;CLEAR "NEW DIRECTORY" FLAG
ADDR DX,DTA
MOV AH,SET_DTA
INT DOS ;SET DTA
ADDR SI,ARG1
ADDR DI,ARG2
COMP_STRINGS ;COMPARE ARG1 AND ARG2
JNE NOTIDENT ;IF NOT IDENTICAL
ERROR 17 ;ARG1 = ARG2, BAD NEWS
MOV AL,17
STC
RET
NOTIDENT:
ADDR CX,SSPATH
ADDR DX,SPREFIX
PUSH BX
ADD BX,ARG1
CALL FIXPATH ;PARSE SOURCE PATHNAME
POP BX
CMP AX,TYPE_UNK
JNE GOODPATH ;IF APPARENTLY OK
ADDR DX,ARG1
CALL ERRORMSG
ERROR 2 ;NONEXISTENT PATHNAME, COMPLAIN
MOV AL,2
STC
RET
GOODPATH:
MOV WORD PTR [BX].SRCTYPE,AX ;SAVE TYPE OF SOURCE
CMP AX,TYPE_DIR ;IS IT A DIRECTORY?
JNE RSET ;NO.
CMP BYTE PTR DS:RFLAG,TRUE ;R FLAG SET?
JE RSET ;IF OK
ADDR DX,ARG1 ;SHOW NAME
CALL ERRORMSG
ERROR 7 ;COMPLAIN ABOUT DIRECTORY
MOV AL,7
STC ;SIGNAL ERROR
RET
RSET:
ADDR SI,ARG2 ;GET POINTER TO DEST
PDEST: PUSH SI ;SAVE IT ON STACK
MOV DI, SI ;AND IN DI
SRCBS: MOV AX,[SI] ;FIND END OF DEST
COMPLIST <0,SPACE,TAB>,GOTEND
INC SI
JMP SRCBS
GOTEND:
DEC SI
CMP [SI],BYTE PTR '\' ;DOES DEST SPEC END W. BACKSLASH?
JNE NOBS ;J/NO, IT DOESN'T
CMP SI, DI ;IS DEST="\"?
JE NOBS ;J/YES, ROOT DIRECTORY, LEAVE IT ALONE
CMP BYTE PTR [SI-1],':' ;IS PREVIOUS CHAR ':'?
JE NOBS ;J/YES, ROOT DIRECTORY, LEAVE IT ALONE
MOV [SI],BYTE PTR 0 ;\ AT END AND NOT ROOT DIR, REMOVE IT
NOBS: COPYPATH ARG2,ARG2A ;MAKE ARG2A = ARG2
ADDR CX, DSPATH
ADDR DX, DPREFIX
POP SI ;GET POINTER TO ARG2 AGAIN
PUSH BX
MOV BX, SI
CALL FIXPATH ;PARSE DEST PATHNAME
POP BX
MOV WORD PTR [BX].DESTTYPE,AX ;SAVE DEST TYPE
CMP AL,TYPE_AFN
JE BADDEST ;DEST CAN'T BE AFN
CMP AL,TYPE_UFN
JNE NOTUFN ;IF DEST TYPE IS UFN,
CMP WORD PTR DS:NUMARGS,2 ;BETTER HAVE ONLY 1 SOURCE
JG BADDEST
CMP WORD PTR DS:SRCTYPE,TYPE_AFN ;AND SOURCE TYPE BETTER NOT BE AFN
JNE NOTUFN
BADDEST:
ERROR 3
MOV AL,3
STC
RET
NOTUFN:
COMPLIST <TYPE_DSP,TYPE_DRV,TYPE_DIR>,DESTDSP ;IF DEST IS DIR OR DRIVE
CMP AL,TYPE_UNK
JNE SHORT DESTOK ;IF DEST EXISTS
DESTNX:
MOV AX,WORD PTR [BX].SRCTYPE ;DEST DOESN'T EXIST
CMP AL,TYPE_DIR ;IF SOURCE IS DIRECTORY,
JE DESTDIR ;MAKE DEST A DIRECTORY, TOO
CMP AL,TYPE_AFN ;SOURCE NOT A DIRECTORY
JE BADDEST ;AFN IS BAD (E.G. MV *.* FOO)
JMP SHORT DESTOK ;OTHERWISE OK
DESTDIR:
CALL SETUP_DEST_DIR ;MAKE DEST A DIRECTORY, TOO
JMP SHORT DESTOK
DESTDSP: MOV AX,WORD PTR [BX].SRCTYPE ;DEST IS DIRECTORY, DIR SPEC OR DRIVE
CMP AL,TYPE_DIR ;IS SOURCE A DIRECTORY? -
JNE DESTOK ;- NO
CALL SETUP_SRC_DIR ;- YES, SOURCE IS DIRECTORY, SET IT UP
JB ERRRET ;IF ERROR
MOV AX,WORD PTR [BX].DESTTYPE
CMP AX,TYPE_DIR ;IS DEST A DIRECTORY? -
JNE DESTOK ;- NO
CALL SETUP_DEST_DIR ;- YES, SET IT UP, TOO
;*******************************
; CHECK FOR INCOMPATIBLE TYPES *
;*******************************
DESTOK:
MOV CX,WORD PTR [BX].DESTTYPE ;GET DEST TYPE
MOV AX,WORD PTR [BX].SRCTYPE ;GET SOURCE TYPE
CMP AX,TYPE_DIR ;IS SOURCE A DIRECTORY? -
JNE TYPEOK ;- NO, IT ISN'T
CMP CX,TYPE_DIR ;- YES, SOURCE IS DIRECTORY,
JE TYPEOK ;DEST BETTER BE DIRECTORY
CMP CX,TYPE_DRV ;OR DRIVE SPEC
JE TYPEOK
ERRRET:
ERROR 3 ;ELSE INCOMPATIBLE TYPE ERROR
MOV AL,3
STC ;ERROR RETURN
RET
TYPEOK: CLC ;NORMAL RETURN
RET
SETUP ENDP
;**********************************************
; MOVE IS FROM DIR TO DIR, DIR SPEC OR DRIVE
; BUILD FULL DEST PATHNAME (IN ARG2A) AND CHECK
; ITS TYPE. (SETS CARRY ON ERROR)
;
SETUP_SRC_DIR PROC NEAR
COPYPATH ARG2,DSPATH ;COPY DEST DIRECTORY OR DRIVE SPEC TO
;DEST PATH (USE AS SCRATCH AREA)
DEC DI ;BACK UP OVER NULL
MOV AX,WORD PTR [BX].DESTTYPE
CMP AX,TYPE_DIR ;SEE IF DEST IS DIRECTORY
JNE NOTDIR ;NOPE
MOV BYTE PTR [DI],'\' ;YES, ADD BACKSLASH TO DEST NAME
INC DI
DEC CX
NOTDIR: ADDR SI,ARG1 ;POINT TO SOURCE DIRECTORY NAME
CMP BYTE PTR [SI+1],':' ;DRIVE SPECIFIED? -
JNE NODRV ;- NO IT WASN'T
ADD SI,2 ;- YES, IT WAS, SKIP IT
NODRV:
; skip to the last element in the source path
MOV DX, SI ;SAVE START OF SOURCE IN DX
STRIP_PATH:
CMP BYTE PTR [SI],0 ;END OF SOURCE?
JE END_OF_SRC ;J/YES
CMP BYTE PTR [SI],'\' ;BACKSLASH IN SOURCE?
JNE NEXT_CHAR ;J/NO, KEEP SEARCHING
MOV DX, SI ;SAVE LOCATION OF BACKSLASH
NEXT_CHAR:
INC SI
JMP STRIP_PATH
END_OF_SRC:
MOV SI, DX ;POINT TO LAST \ OR START OF STRING
CMP BYTE PTR [SI],'\' ;IS NEXT CHAR. \?
JNE NOBKSL ;NO
INC SI ;YES, SKIP PAST IT
NOBKSL:
CALL CPYCNT ;COPY LAST PART OF SOURCE DIRECTORY
;NAME TO DEST PATH
COPYPATH DSPATH,ARG2A ;COPY AUGMENTED DEST PATH TO ARG2A
ADDR CX,DSPATH
ADDR DX,DPREFIX
PUSH BX
ADD BX,ARG2A
CALL FIXPATH ;PARSE FULL DEST PATHNAME
POP BX
MOV WORD PTR [BX].DESTTYPE,AX ;SAVE DEST TYPE
CMP AL,TYPE_UNK
JNE DESTXST ;IF DEST EXISTS
MOV AX,WORD PTR [BX].SRCTYPE ;DEST DOESN'T EXIST, GET SOURCE TYPE
CMP AX,TYPE_DIR ;IF SOURCE IS A DIRECTORY,
JNE DESTXST
MOV [BX].DESTTYPE,TYPE_DIR ;MAKE DEST A DIRECTORY, TOO
DESTXST:
CLC
RET
SETUP_SRC_DIR ENDP
;***************************************************************
; THIS PROCEDURE IS CALLED IF THE DESTINATION IS A NEW DIRECTORY
;
SETUP_DEST_DIR PROC NEAR
ADDR SI,DSPATH
ADDR DI,DPREFIX
PUSH DI
MOV CX,PATHSIZE
CALL CPYCNT ;COPY SEARCH PATH TO PREFIX
POP DI
MOV CX,PATHSIZE
MOV AL,0
REPNE SCASB ;FIND END OF DEST PREFIX
DEC DI
MOV [DI],BYTE PTR '\' ;ADD BACKSLASH
MOV [DI+1],BYTE PTR 0
MOV [BX].NEWDIR,WORD PTR 1 ;SET "NEW DIRECTORY FLAG"
MOV WORD PTR [BX].DESTTYPE,TYPE_DIR ;SET DEST TYPE = DIRECTORY
RET
SETUP_DEST_DIR ENDP
PAGE +
;****************************************************************************
; THIS ROUTINE TAKES THE INFO ON A FILE IN THE DTA AND USES IT TO GENERATE
; FULL, UNAMBIGUOUS SOURCE AND DESTINATION FILE NAMES BY ADDING THE NECESSARY
; DIRECTORY PREFIXES TO IT. IT SETS THE CARRY FLAG IF THE "FILE"
; IS "." OR "..", OR IF THE SOURCE IS IDENTICAL TO THE DESTINATION.
;
BUILD_NAMES PROC NEAR
COPYPATH SPREFIX,SRCNAME ;GET SOURCE PREFIX, COPY TO SOURCE NAME
DEC DI ;BACK UP OVER NULL
ADDR SI,(DTA+30) ;POINT TO FILE NAME WE FOUND
CMP [SI],BYTE PTR '.' ;DOES IT START WITH .? -
JNE NOTDOT ;- NO
STC ;- YES, SET CARRY TO SKIP IT
RET
NOTDOT:
CALL CPYCNT ;ADD IT TO SOURCE PREFIX
CMP WORD PTR [BX].DESTTYPE,TYPE_UFN ;IS DEST A UFN?
JE DESTUFN ;YES, JUST USE IT
CMP WORD PTR [BX].DESTTYPE,TYPE_UNK
JE DESTUFN ;DITTO IF DEST DOESN'T EXIST
;IF NOT, MAKE DEST NAME = SOURCE NAME
COPYPATH DPREFIX,DESTNAME ;COPY DEST PREFIX TO DEST NAME
DEC DI ;BACK UP OVER NULL
ADDR SI,DTA+30 ;POINT TO FILE NAME WE FOUND
CALL CPYCNT ;ADD IT TO DEST PREFIX
JMP SHORT DONE ;GO TO NEXT STEP
DESTUFN: ;DEST IS A FILE, NOT A DIRECTORY
COPYPATH ARG2,DESTNAME ;SO DESTNAME JUST = ARG2
DONE:
CALL CHECK_NAMES ;CHECK SOURCE AND DEST FOR IDENTITY
RET
BUILD_NAMES ENDP
PAGE +
;*********************************************************************
; THIS PROCEDURE CHECKS THE SOURCE AND DEST FILES, AND SETS THE CARRY
; FLAG IF THEY ARE IDENTICAL
;
CHECK_NAMES PROC NEAR
MOV BX,WORD PTR RBASE ;GET ADDR. OF DATA STRUCTURE
MOV AH,SET_DTA
MOV DX,OFFSET DEST_DTA
INT DOS ;SET DTA
ADDR DX,DESTNAME ;POINT TO DESTINATION
MOV CX,0FFH
MOV AH,FIND_FIRST
INT DOS ;SEARCH FOR IT
PUSHF
ADDR DX,DTA
MOV AH,SET_DTA
INT DOS ;RESET DTA
POPF
JNB GOTDEST ;IF FOUND IT
CLC ;OK IF IT DOESN'T EXIST
RET
GOTDEST: ADDR SI,DTA+21 ;POINT TO INFO ON SOURCE
MOV DI,OFFSET DEST_DTA+21 ;POINT TO INFO ON DEST
MOV CX,9 ;SIZE OF FILE ATTRIBUTES
REPE CMPSB ;COMPARE SOURCE AND DEST
JNE NOTEQU ;IF NOT =
COMP_STRINGS ;COMPARE STRINGS
JNE NOTEQU ;IF SOURCE AND DEST NAMES NOT EQUAL
STC ;FILES ARE EQUAL (NAMES AND ATTRIB.)
RET
NOTEQU: CLC
RET
CHECK_NAMES ENDP
PAGE +
;**************************************************************************
; THIS ROUTINE COPIES A SINGLE FILE (SRCNAME) TO ITS DESTINATION (DESTNAME)
; ON ANOTHER DISK.
;
; IT SETS THE CARRY FLAG IF AN ERROR OCCURRED.
;
COPY_FILE PROC NEAR
ADDR DX,SRCNAME ;DX = SOURCE FILE NAME
MOV AL,READ_ACCESS
MOV AH,DOS2_OPEN
INT DOS ;TRY TO OPEN SOURCE FILE
JC FAIL ;IF CAN'T
MOV WORD PTR INHANDLE,AX ;SAVE FILE HANDLE
CALL GETOK ;GET CONFIRMATION IF DEST EXISTS
JNC OKGIVEN ;IF OK TO DELETE
MOV BX,WORD PTR INHANDLE ;IF NOT OK, GET FILE HANDLE
MOV AH,DOS2_CLOSE
INT DOS ;CLOSE INPUT FILE
CLC
RET ;EXIT W/O MOVING
FAIL: ADDR DX,SRCNAME ;POINT TO SOURCE FILE NAME
CALL ERRORMSG ;SHOW IT
ERROR 5 ;COMPLAIN THAT CAN'T OPEN
MOV AL,5
STC
RET
OKGIVEN: ADDR DX,DESTNAME
MOV CL,BYTE PTR [BX].DTA+21 ;MAKE DEST ATTRIBUTES=SOURCE ATTRIB.
MOV CH,0
MOV AH,DOS2_CREATE
INT DOS ;CREATE FILE (OR TRUNCATE OLD ONE)
JNC CREATED ;IF OK
ADDR DX,DESTNAME
CALL ERRORMSG ;CREATE FAILED, SHOW FILE NAME
ERROR 5 ;COMPLAIN THAT CAN'T OPEN
MOV AL,5
STC
RET
CREATED:
MOV WORD PTR OUTHANDLE,AX ;SAVE OUTPUT HANDLE
COPYLOOP:
PUSH BX
MOV BX,WORD PTR INHANDLE ;BX = FILE HANDLE
MOV DX,OFFSET BUFFER ;DX = BUFFER OFFSET
MOV CX,BUFSIZE ;CX = BUFFER SIZE
MOV AH,READ ;USING DOS 2 CALL,
INT DOS ;READ FROM SOURCE
POP BX
JC RDERR ;IF ERROR
MOV CX,AX ;SAVE AMT. READ
CMP AX,0
JE ENDCOPY ;IF 0
PUSH BX
MOV BX,WORD PTR OUTHANDLE ;BX = FILE HANDLE
MOV DX,OFFSET BUFFER ;DX = BUFFER OFFSET
MOV AH,WRITE
INT DOS ;WRITE BUFFER TO DEST
POP BX
JC WRERR ;IF ERROR
CMP AX,CX ;WERE ALL BYTES WRITTEN?
JE COPYLOOP ;YES, KEEP GOING
ADDR DX,DESTNAME ;POINT TO DEST NAME
CALL ERRORMSG ;SHOW IT
ERROR 10 ;PROBABLY DISK FULL
MOV AL,10 ;SET ERROR CODE
JMP SCRATCH ;ABORT
WRERR: ADDR DX,DESTNAME ;POINT TO DEST NAME
CALL ERRORMSG ;SHOW IT
ERROR 8 ;WRITE ERROR
MOV AL,8 ;SET ERROR CODE
JMP SCRATCH ;ABORT
RDERR: ADDR DX,SRCNAME ;POINT TO SOURCE NAME
CALL ERRORMSG ;SHOW IT
ERROR 9 ;READ ERROR
MOV AL,9 ;SET ERROR CODE
JMP SCRATCH ;ABORT
ENDCOPY: PUSH BX ;NO ERRORS
MOV BX,WORD PTR INHANDLE ;GET INPUT FILE HANDLE
MOV AH,FILE_TIMES
MOV AL,0 ;GET DATE AND TIME
INT DOS ;GET IT
MOV BX,WORD PTR OUTHANDLE ;GET OUTPUT FILE HANDLE
MOV AH,FILE_TIMES
MOV AL,1
INT DOS ;SET OUTPUT DATE AND TIME SAME AS INPUT
MOV BX,WORD PTR INHANDLE
MOV AH,DOS2_CLOSE
INT DOS ;CLOSE INPUT FILE
MOV BX,WORD PTR OUTHANDLE
MOV AH,DOS2_CLOSE
INT DOS ;CLOSE OUTPUT FILE
POP BX
ADDR DX,SRCNAME ;POINT TO SOURCE NAME
CALL UNLINK2 ;REMOVE IT
JB DELERR ;IF SOME PROBLEM
RET ;ALL DONE
DELERR: ADDR DX,SRCNAME ;CAN'T DELETE FILE, SHOW FILE NAME
CALL ERRORMSG
ERROR 13 ;AND ERROR MSG.
MOV AL,13
STC
RET
SCRATCH:
PUSH AX ;SAVE ERROR CODE
PUSH BX
MOV BX,WORD PTR OUTHANDLE
MOV AH,DOS2_CLOSE
INT DOS ;CLOSE OUTPUT FILE
POP BX
ADDR DX,DESTNAME
CALL UNLINK2 ;REMOVE PARTIAL FILE
POP AX
STC
RET
COPY_FILE ENDP
PAGE +
;*******************************************************************
; THIS ROUTINE "MOVES" A FILE (SRCNAME) BY RENAMING IT (TO DESTNAME)
;
; IT SETS THE CARRY FLAG IF AN ERROR OCCURRED.
;
RENAME_FILE PROC NEAR
CALL GETOK ;SEE IF FILE EXISTS
JNC OK ;USER SAID OK TO ERASE
MOV AL,4
STC
RET ;NOT OK, SKIP THIS ONE
OK: ADDR DX,SRCNAME ;POINT TO SOURCE
ADDR DI,DESTNAME ;AND DEST
MOV AH,DOS2_RENAME
PUSH BX
INT DOS ;RENAME
POP BX
JNC RENOK ;IF OK
ADDR DX,SRCNAME
CALL ERRORMSG ;SHOW SOURCE
ERROR 11 ;SHOULDN'T HAPPEN
MOV AL,11 ;SET ERROR CODE
STC ;SIGNAL ERROR
RENOK: RET
RENAME_FILE ENDP
PAGE +
;*******************************************************************
; THIS ROUTINE CHECKS TO SEE IF THE DEST FILE EXISTS.
; IF IT DOES, AND IF THE CONFIRM FLAG IS SET, IT ASKS
; FOR CONFIRMATION BEFORE DELETING THE FILE. IF THE
; CONFIRM FLAG IS NOT SET, IT JUST DELETES IT.
; ON EXIT, 'C'=1 IF THE USER REPLIED 'N' TO THE CONFIRM MSG.
;
GETOK PROC NEAR
ADDR DX,DESTNAME
MOV AL,0
MOV AH,CHMOD
INT DOS ;GET ATTRIBUTES FOR DEST
JNC EXISTS ;IF FILE EXISTS
CLC
RET
EXISTS:
CMP BYTE PTR DS:IFLAG,TRUE ;IS FLAG SET TO CONFIRM?
JNE COK ;NO.
ADDR DX,DESTNAME
CALL ERRORMSG ;SHOW DEST FILE
ERROR 4 ;ASK FOR CONFIRMATION
CALL GETYORN ;GET Y OR N RESPONSE
CMP AL,'Y' ;IS IT YES?
JNE ABEXIT ;NO, SIGNAL CALLING ROUTINE
COK:
TRYAGAIN:
ADDR DX,DESTNAME
MOV AH,UNLINK
INT DOS ;DELETE THE FILE
JNC OKEXIT ;IF NO PROBLEM
CMP AX,5 ;WAS FILE R/O? (THIS OUGHT TO BE
; THE ONLY POSSIBLE ERROR)
JNE OKEXIT ;EXIT IF SOME OTHER ERROR
ADDR DX,DESTNAME
CALL ERRORMSG ;SHOW DEST FILE NAME
ERROR 6 ;R/O FILE, QUERY USER AGAIN
CALL GETYORN ;GET Y OR N RESPONSE
CMP AL,'Y' ;TEST FOR 'Y' RESPONSE
JNE ABEXIT ;ABORT IF USER SAYS 'N'
MOV CX,0 ;'Y' TYPED CLEAR ALL ATTRIBUTES
ADDR DX,DESTNAME ;POINT TO FILE NAME
MOV AH,CHMOD
MOV AL,1 ;CHANGE ATTRIBUTES
INT DOS ;TRY MAKING IT R/W
JMP TRYAGAIN ;TRY TO ERASE IT AGAIN
ABEXIT: STC
RET
OKEXIT: CLC
RET
GETOK ENDP
; PRTERR = PRINT ERROR MESSAGE (DX POINTS TO OFFSET FROM DGROUP)
PRTERR: PUSH DS
MOV AX,DGROUP
MOV DS,AX
CALL ERRORMSG
POP DS
RET
CLOSE2: MOV BX,WORD PTR OUTHANDLE
MOV AH,DOS2_CLOSE
INT DOS ;CLOSE OUTPUT FILE
RET
CLOSE1:
MOV BX,WORD PTR INHANDLE
MOV AH,DOS2_CLOSE
INT DOS ;CLOSE INPUT FILE
RET
EXIT2:
MOV AH,EXIT
INT DOS ;EXIT TO DOS
END ENTRY